package ar.uba.fi.tonyvaliente.documents;

import java.util.logging.Logger;

import ar.uba.fi.tonyvaliente.files.File;
import ar.uba.fi.tonyvaliente.files.FileBlock;
import ar.uba.fi.tonyvaliente.files.FileException;
import ar.uba.fi.tonyvaliente.files.FileRecord;
import ar.uba.fi.tonyvaliente.files.PersistorException;
import ar.uba.fi.tonyvaliente.files.RecordPosition;
import ar.uba.fi.tonyvaliente.tree.BPlusTreeNode;

/**
 * Clase que envuelve un archivo de bloques (File) donde están las listas de términos
 * @author Santiago
 */
public class TermListFileWrapper {
	
	private static Logger log = Logger.getLogger(TermListFileWrapper.class.getName());
	
	private File file;
	
	/**
	 * Crea una nueva instancia de la clase que apunta a un archivo de bloques
	 * donde están las listas de términos 
	 * @param file Archivo de bloques donde se encuentra el arbol B+
	 */
	public TermListFileWrapper(File file) {
		this.file = file;
	}
	
	/**
	 * Cierra el archivo encapsulado
	 * @throws DocumentIndexException si ocurre un error al cerrar el archivo
	 */
	public void close() throws DocumentIndexException {
		try {
			file.close();
		} catch (FileException e) {
			throw new DocumentIndexException("Error al cerrar el BPlusTreeFileWrapper", e);
		}
	}
	
	/**
	 * Obtiene el nodo cuyo bloque y offset dentro del bloque recibe como parametro.
	 * 
	 * @param blockNumber posicion del bloque dentro del File
	 * @param offset posicion del nodo serializado dentro del bloque
	 * @return nodo recuperado
	 */
	public byte[] getTermList(RecordPosition pos) throws DocumentIndexException {
		try {
			log.fine("getTermList IN");
			log.finer("BlockNumber: " + pos.getBlockNumber());
			log.finer("NodeOffset: " + pos.getOffset());
			
			FileBlock block = file.getBlock(pos.getBlockNumber());
			FileRecord record = block.getRecord(pos.getOffset());
			return record.getData();
		} catch (FileException e) {
			throw new DocumentIndexException("Error al acceder al archivo para obtener la lista de términos", e);
		}
	}
	
	/**
	 * Agrega una lista de términos al archivo.
	 * Como las listas están codificadas en delta son byte[]
	 * @param termList lista a agregar
	 * @return RecordPosition que contiene el numero de bloque y offset
	 * donde se escribio la lista
	 */
	public RecordPosition appendTermList(byte[] termList) throws DocumentIndexException {
		try {
			
			log.fine("appendTermList IN");
			
			FileRecord r = new FileRecord(termList);
			RecordPosition posicion = file.addRecord(r);
			log.finer("posicion: block: " + posicion.getBlockNumber() + " offset:  " + posicion.getOffset());
			
			return posicion;
		} catch (FileException e) {
			throw new DocumentIndexException("Error al acceder al archivo para agregar la lista de términos", e);
		}
		
	}
	
	/**
	 * Escribe una lista de términos en el archivo, en la posicion indicada por parametro
	 * Como las listas están codificadas en delta son byte[]
	 * @param termList Lista a actualizar en archivo
	 * @param pos Posicion del archivo donde escribir el nodo
	 * @return TRUE si pudo escribir el nodo, FALSE si el bloque indicado en pos estaba lleno
	 * y no pudo hacerse la actualizacion
	 * @throws DocumentIndexException Si ocurre un error al acceder a archivo o serializar el nodo
	 */
	public RecordPosition updateTermList(byte[] termList, RecordPosition pos) throws DocumentIndexException {
			
			log.fine("updateTermList IN");
			log.finer("TermListBlockNumber: " + pos.getBlockNumber());
			log.finer("TermListOffset: " + pos.getOffset());
				
			
		try{
			// Intentamos actualizar la lista
			FileBlock block = file.getBlock(pos.getBlockNumber());
			FileRecord r = new FileRecord(termList);
			block.updateRecord(pos.getOffset(), r);
			file.updateBlock(pos.getBlockNumber(), block);
			return pos;
		} catch (FileException e) {
			// Si falla intentamos agregar un nuevo nodo
			// Si vuelve a fallar se aborta el proceso
			RecordPosition newPos = this.appendTermList(termList);
			return newPos;
		}
	}
}
